package com.wiscamp.ninechapters.tags.domain.services;


import com.wiscamp.ninechapters.tags.domain.TagsErrorCodes;
import com.wiscamp.ninechapters.tags.domain.models.Subject;
import com.wiscamp.ninechapters.tags.domain.repositories.SubjectRepository;
import cube.common.cache.CacheManagerNames;
import cube.ddd.aspect.HandleServiceException;
import cube.ddd.domain.BaseDomainService;
import cube.ddd.exception.ExceptionUtils;
import jakarta.validation.constraints.NotNull;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.Objects;

/**
 * Subject Service
 *
 * @author yifeng
 */
@Service
public class SubjectService extends BaseDomainService {

    private final SubjectRepository subjectRepository;

    private final String CACHE_NAME = "subjects";

    public SubjectService(SubjectRepository repository) {
        this.subjectRepository = repository;
    }

    /**
     * Gets a subject
     *
     * @param subjectId
     * @return
     */
    @Cacheable(value = CACHE_NAME, key = "#subjectId", cacheManager = CacheManagerNames.SIXTY_MINUTES_CACHE)
    @HandleServiceException
    public Subject getSubject(long subjectId) {
        var subject = subjectRepository.getSubject(subjectId);
        if (Objects.isNull(subject)) {
            ExceptionUtils.throwServiceException(TagsErrorCodes.SUBJECT_NOT_FOUND, subjectId);
        }
        return subject;
    }

    /**
     * Creates a subject
     *
     * @param subject
     * @return
     */
    @CachePut(value = CACHE_NAME, key = "#subject.subjectId", cacheManager = CacheManagerNames.SIXTY_MINUTES_CACHE)
    @HandleServiceException
    public Subject createSubject(@NotNull Subject subject, long operatorId) {
        var existedSubject = subjectRepository.getSubjectByCode(subject.getSubjectCode());
        if (Objects.nonNull(existedSubject)) {
            ExceptionUtils.throwServiceException(TagsErrorCodes.SUBJECT_CODE_EXISTED, subject);
        }

        subject.createBy(operatorId);
        return subjectRepository.saveSubject(subject);
    }

    /**
     * Updates a subject
     *
     * @param subject
     * @return
     */
    @CachePut(value = CACHE_NAME, key = "#subject.subjectId", cacheManager = CacheManagerNames.SIXTY_MINUTES_CACHE)
    @HandleServiceException
    public Subject updateSubject(@NotNull Subject subject, long operatorId) {
        var existedSubject = getSubject(subject.getSubjectId());
        existedSubject = subjectRepository.getSubjectByCode(subject.getSubjectCode());
        if (Objects.nonNull(existedSubject) && existedSubject.getSubjectId() != subject.getSubjectId()) {
            ExceptionUtils.throwServiceException(TagsErrorCodes.SUBJECT_CODE_EXISTED, subject);
        }
        subject.updateBy(operatorId);
        return subjectRepository.saveSubject(subject);
    }

    /**
     * Deletes a subject
     *
     * @param subjectId
     * @return
     */
    @CacheEvict(value = CACHE_NAME, key = "#subjectId")
    @HandleServiceException
    public boolean deleteSubject(long subjectId, long operatorId) {
        var subject = getSubject(subjectId);
        if (Objects.isNull(subject)) return false;
        subject.delete();
        subject.updateBy(operatorId);
        subjectRepository.saveSubject(subject);
        return true;
    }

    /**
     * enable a subject
     *
     * @param subjectId
     * @return
     */
    @CacheEvict(value = CACHE_NAME, key = "#subjectId")
    @HandleServiceException
    public boolean enableSubject(long subjectId, long operatorId) {
        var subject = getSubject(subjectId);
        if (Objects.isNull(subject)) return false;
        subject.enable();
        subject.updateBy(operatorId);
        subjectRepository.saveSubject(subject);
        return true;
    }

    /**
     * disable a subject
     *
     * @param subjectId
     * @return
     */
    @CacheEvict(value = CACHE_NAME, key = "#subjectId")
    @HandleServiceException
    public boolean disableSubject(long subjectId, long operatorId) {
        var subject = getSubject(subjectId);
        if (Objects.isNull(subject)) return false;
        subject.disable();
        subject.updateBy(operatorId);
        subjectRepository.saveSubject(subject);
        return true;
    }
}
